{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Lesson 08 - Python and IPython Review\n",
    "\n",
    "### Readings\n",
    "\n",
    "* McKinney: [Chapter 1. Preliminaries](http://proquest.safaribooksonline.com/book/programming/python/9781491957653/preliminaries/intro_html)\n",
    "* McKinney: [Chapter 2. Python Language Basics, IPython, and Jupyter Notebooks](http://proquest.safaribooksonline.com/book/programming/python/9781491957653/python-language-basics-ipython-and-jupyter-notebooks/intro_python_environment_html)\n",
    "* McKinney: [Chapter 3. Built-in Data Structures, Functions, and Files](http://proquest.safaribooksonline.com/book/programming/python/9781491957653/built-in-data-structures-functions-and-files/intro_python_stdlib_html)\n",
    "\n",
    "### Table of Contents\n",
    "\n",
    "* [Python Review](#python)\n",
    "* [Python Script Example](#script)\n",
    "* [IPython Review](#ipython)\n",
    "* [Jupyter Notebook Command Review](#jupyter)\n",
    "* [Star Operator](#star)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<a id=\"python\"></a>\n",
    "\n",
    "### Python Review\n",
    "\n",
    "Plus a few new features...\n",
    "\n",
    "For this lesson, we will open up an IPython terminal and follow along.\n",
    "\n",
    "* `source activate python2` (want Python 2 because dicts work differently)\n",
    "* `ipython`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### List Methods\n",
    "\n",
    "from [Python Documentation](https://docs.python.org/2/tutorial/datastructures.html)\n",
    "\n",
    "Here are all of the methods of list objects:\n",
    "\n",
    "**list.append(x)**\n",
    "Add an item to the end of the list; equivalent to a[len(a):] = [x].\n",
    "\n",
    "**list.extend(L)**\n",
    "Extend the list by appending all the items in the given list; equivalent to a[len(a):] = L.\n",
    "\n",
    "**list.insert(i, x)**\n",
    "Insert an item at a given position. The first argument is the index of the element before which to insert, so a.insert(0, x) inserts at the front of the list, and a.insert(len(a), x) is equivalent to a.append(x).\n",
    "\n",
    "**list.remove(x)**\n",
    "Remove the first item from the list whose value is x. It is an error if there is no such item.\n",
    "\n",
    "**list.pop([i])**\n",
    "Remove the item at the given position in the list, and return it. If no index is specified, a.pop() removes and returns the last item in the list. (The square brackets around the i in the method signature denote that the parameter is optional, not that you should type square brackets at that position. You will see this notation frequently in the Python Library Reference.)\n",
    "\n",
    "**list.index(x)**\n",
    "Return the index in the list of the first item whose value is x. It is an error if there is no such item.\n",
    "\n",
    "**list.count(x)**\n",
    "Return the number of times x appears in the list.\n",
    "\n",
    "**list.sort(cmp=None, key=None, reverse=False)**\n",
    "Sort the items of the list in place (the arguments can be used for sort customization, see sorted() for their explanation).\n",
    "\n",
    "**list.reverse()**\n",
    "Reverse the elements of the list, in place.\n",
    "\n",
    "An example that uses most of the list methods:\n",
    "\n",
    "\t>>> a = [66.25, 333, 333, 1, 1234.5]\n",
    "\t>>> print(a.count(333), a.count(66.25), a.count('x'))\n",
    "\t2 1 0\n",
    "\t>>> a.insert(2, -1)\n",
    "\t>>> a.append(333)\n",
    "\t>>> a\n",
    "\t[66.25, 333, -1, 333, 1, 1234.5, 333]\n",
    "\t>>> a.index(333)\n",
    "\t1\n",
    "\t>>> a.remove(333)\n",
    "\t>>> a\n",
    "\t[66.25, -1, 333, 1, 1234.5, 333]\n",
    "\t>>> a.reverse()\n",
    "\t>>> a\n",
    "\t[333, 1234.5, 1, 333, -1, 66.25]\n",
    "\t>>> a.sort()\n",
    "\t>>> a\n",
    "\t[-1, 1, 66.25, 333, 333, 1234.5]\n",
    "\t>>> a.pop()\n",
    "\t1234.5\n",
    "\t>>> a\n",
    "\t[-1, 1, 66.25, 333, 333]\n",
    "\n",
    "You might have noticed that methods like insert, remove or sort that only modify the list have no return value printed -- they return the default None. This is a design principle for all mutable data structures in Python."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Dict Operations\n",
    "\n",
    "from [Python Documentation](https://docs.python.org/3/tutorial/datastructures.html) \n",
    "\n",
    "Another useful data type built into Python is the dictionary (see Mapping Types — dict). Dictionaries are sometimes found in other languages as “associative memories” or “associative arrays”. Unlike sequences, which are indexed by a range of numbers, dictionaries are indexed by keys, which can be any immutable type; strings and numbers can always be keys. Tuples can be used as keys if they contain only strings, numbers, or tuples; if a tuple contains any mutable object either directly or indirectly, it cannot be used as a key. You can’t use lists as keys, since lists can be modified in place using index assignments, slice assignments, or methods like append() and extend().\n",
    "\n",
    "It is best to think of a dictionary as an unordered set of key: value pairs, with the requirement that the keys are unique (within one dictionary). A pair of braces creates an empty dictionary: {}. Placing a comma-separated list of key:value pairs within the braces adds initial key:value pairs to the dictionary; this is also the way dictionaries are written on output.\n",
    "\n",
    "The main operations on a dictionary are storing a value with some key and extracting the value given the key. It is also possible to delete a key:value pair with del. If you store using a key that is already in use, the old value associated with that key is forgotten. It is an error to extract a value using a non-existent key.\n",
    "\n",
    "**dict.keys()** returns a list of all the keys used in the dictionary, in arbitrary order (if you want it sorted, just apply the sorted() function to it). To check whether a single key is in the dictionary, use the in keyword.\n",
    "\n",
    "**dict.items()** returns a copy of the dictionary’s list of (key, value) pairs as tuples.\n",
    "\n",
    "Here is a small example using a dictionary:\n",
    "\n",
    "\t>>>\n",
    "\t>>> tel = {'jack': 4098, 'sape': 4139}\n",
    "\t>>> tel['guido'] = 4127\n",
    "\t>>> tel\n",
    "\t{'sape': 4139, 'guido': 4127, 'jack': 4098}\n",
    "\t>>> tel['jack']\n",
    "\t4098\n",
    "\t>>> del tel['sape']\n",
    "\t>>> tel['irv'] = 4127\n",
    "\t>>> tel\n",
    "\t{'guido': 4127, 'irv': 4127, 'jack': 4098}\n",
    "\t>>> tel.keys()\n",
    "\t['guido', 'irv', 'jack']\n",
    "\t>>> 'guido' in tel\n",
    "\tTrue\n",
    "    >>> tel.items()\n",
    "    dict_items([('guido', 4127), ('irv', 4127), ('jack', 4098)])\n",
    "    >>> list(tel.items())\n",
    "    [('guido', 4127), ('irv', 4127), ('jack', 4098)]\n",
    "\n",
    "The dict() constructor builds dictionaries directly from sequences of key-value pairs:\n",
    "\n",
    "\t>>>\n",
    "\t>>> dict([('sape', 4139), ('guido', 4127), ('jack', 4098)])\n",
    "\t{'sape': 4139, 'jack': 4098, 'guido': 4127}\n",
    "\n",
    "In addition, dict comprehensions can be used to create dictionaries from arbitrary key and value expressions:\n",
    "\n",
    "\t>>>\n",
    "\t>>> {x: x**2 for x in (2, 4, 6)}\n",
    "\t{2: 4, 4: 16, 6: 36}\n",
    "\n",
    "When the keys are simple strings, it is sometimes easier to specify pairs using keyword arguments:\n",
    "\n",
    "\t>>>\n",
    "\t>>> dict(sape=4139, guido=4127, jack=4098)\n",
    "\t{'sape': 4139, 'jack': 4098, 'guido': 4127}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Ordered Dicts\n",
    "\n",
    "Dicts are inherently unordered (though may appear ordered in Python 3.7), but there are ways to order them if you need to. If you wanted to print the contents of a dict in its natural disordered state, you could write:\n",
    "\n",
    "\tfor key, value in mydict.items():\n",
    "\t    print(key, value)\n",
    "\n",
    "To print the dict sorted by key, you can retrieve the keys as a list, sort them, and then print each key-value pair one by one.\n",
    "\n",
    "\tD = {'a': 1, 'b': 2, 'c': 3}\n",
    "\tsorted_keys = D.keys()   # Unordered keys list\n",
    "\tsorted_keys.sort()       # Sorted keys list\n",
    "\tfor key in sorted_keys:  # Iterate through sorted keys\n",
    "\t    print(key, '=>', D[key])\n",
    "\t    \n",
    "The sorted function for dicts saves some time by generated the list of sorted keys.\n",
    "\t\n",
    "\tsorted_keys = sorted(D)\n",
    "\tfor key in sorted_keys:\n",
    "\t    print(key, '=>', D[key])\n",
    "\t    \n",
    "Whether you sort your dict or not, being able to print it out or iterate through it is useful for debugging and in your programs.\n",
    "\n",
    "If you really want a dict to be ordered, and not just *printed* in order using a list of sorted keys, you can use the OrderedDict module.\n",
    "\n",
    "\tfrom collections import OrderedDict\n",
    "\t\n",
    "\t# Regular dictionary:\n",
    "\td = {}\n",
    "\td['a'] = 'A'\n",
    "\td['b'] = 'B'\n",
    "\td['c'] = 'C'\n",
    "\td['d'] = 'D'\n",
    "\td['e'] = 'E'\n",
    "\t\n",
    "\tfor k, v in d.items():\n",
    "\t    print(k, v)\n",
    "\t\n",
    "\t# Ordered dictionary:\n",
    "\td = OrderedDict()\n",
    "\td['a'] = 'A'\n",
    "\td['b'] = 'B'\n",
    "\td['c'] = 'C'\n",
    "\td['d'] = 'D'\n",
    "\td['e'] = 'E'\n",
    "\t\n",
    "\tfor k, v in d.items():\n",
    "\t    print(k, v)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Looping Techniques\n",
    "\n",
    "from [Python Documentation](https://docs.python.org/3/tutorial/datastructures.html)\n",
    "\n",
    "When looping through a sequence, the position index and corresponding value can be retrieved at the same time using the enumerate() function.\n",
    "\n",
    "\t>>>\n",
    "\t>>> for i, v in enumerate(['tic', 'tac', 'toe']):\n",
    "\t...     print(i, v)\n",
    "\t...\n",
    "\t0 tic\n",
    "\t1 tac\n",
    "\t2 toe\n",
    "\n",
    "To loop over two or more sequences at the same time, the entries can be paired with the zip() function.\n",
    "\n",
    "\t>>>\n",
    "\t>>> questions = ['name', 'quest', 'favorite color']\n",
    "\t>>> answers = ['lancelot', 'the holy grail', 'blue']\n",
    "\t>>> for q, a in zip(questions, answers):\n",
    "\t...     print('What is your {0}?  It is {1}.'.format(q, a))\n",
    "\t...\n",
    "\tWhat is your name?  It is lancelot.\n",
    "\tWhat is your quest?  It is the holy grail.\n",
    "\tWhat is your favorite color?  It is blue.\n",
    "\n",
    "To make a dictionary from those two lists:\n",
    "\n",
    "\t>>>\n",
    "\t>>> foo = dict(zip(questions, answers))\n",
    "\t>>> foo\n",
    "\t{'quest': 'the holy grail', 'name': 'lancelot', 'favorite color': 'blue'}\n",
    "\n",
    "To loop over a sequence in reverse, first specify the sequence in a forward direction and then call the reversed() function.\n",
    "\n",
    "\t>>>\n",
    "\t>>> for i in reversed(range(1,10,2)):\n",
    "\t...     print(i)\n",
    "\t...\n",
    "\t9\n",
    "\t7\n",
    "\t5\n",
    "\t3\n",
    "\t1\n",
    "\n",
    "To loop over a sequence in sorted order, use the sorted() function which returns a new sorted list while leaving the source unaltered.\n",
    "\n",
    "\t>>>\n",
    "\t>>> basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']\n",
    "\t>>> for f in sorted(set(basket)):\n",
    "\t...     print(f)\n",
    "\t...\n",
    "\tapple\n",
    "\tbanana\n",
    "\torange\n",
    "\tpear\n",
    "\n",
    "When looping through dictionaries, the key and corresponding value can be retrieved at the same time using the items() method.\n",
    "\n",
    "\t>>>\n",
    "\t>>> knights = {'gallahad': 'the pure', 'robin': 'the brave'}\n",
    "\t>>> for k, v in knights.items():\n",
    "\t...     print(k, v)\n",
    "\t...\n",
    "\tgallahad the pure\n",
    "\trobin the brave\n",
    "\n",
    "It is sometimes tempting to change a list while you are looping over it; however, it is often simpler and safer to create a new list instead.\n",
    "\n",
    "\t>>>\n",
    "\t>>> import math\n",
    "\t>>> raw_data = [56.2, float('NaN'), 51.7, 55.3, 52.5, float('NaN'), 47.8]\n",
    "\t>>> filtered_data = []\n",
    "\t>>> for value in raw_data:\n",
    "\t...     if not math.isnan(value):\n",
    "\t...         filtered_data.append(value)\n",
    "\t...\n",
    "\t>>> filtered_data\n",
    "\t[56.2, 51.7, 55.3, 52.5, 47.8]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### List Comprehensions\n",
    "\n",
    "from [Python Documentation](https://docs.python.org/2/tutorial/datastructures.html)\n",
    "\n",
    "List comprehensions provide a concise way to create lists. Common applications are to make new lists where each element is the result of some operations applied to each member of another sequence or iterable, or to create a subsequence of those elements that satisfy a certain condition.\n",
    "\n",
    "For example, assume we want to create a list of squares, like:\n",
    "\n",
    "\t>>>\n",
    "\t>>> squares = []\n",
    "\t>>> for x in range(10):\n",
    "\t...     squares.append(x**2)\n",
    "\t...\n",
    "\t>>> squares\n",
    "\t[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]\n",
    "\n",
    "We can obtain the same result with:\n",
    "\n",
    "\tsquares = [x**2 for x in range(10)]\n",
    "\n",
    "This is also equivalent to `squares = map(lambda x: x**2, range(10))`, but it’s more concise and readable.\n",
    "\n",
    "A list comprehension consists of brackets containing an expression followed by a for clause, then zero or more for or if clauses. The result will be a new list resulting from evaluating the expression in the context of the for and if clauses which follow it. For example, this listcomp combines the elements of two lists if they are not equal:\n",
    "\n",
    "\t>>>\n",
    "\t>>> [(x, y) for x in [1,2,3] for y in [3,1,4] if x != y]\n",
    "\t[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]\n",
    "\n",
    "and it’s equivalent to:\n",
    "\n",
    "\t>>>\n",
    "\t>>> combs = []\n",
    "\t>>> for x in [1,2,3]:\n",
    "\t...     for y in [3,1,4]:\n",
    "\t...         if x != y:\n",
    "\t...             combs.append((x, y))\n",
    "\t...\n",
    "\t>>> combs\n",
    "\t[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]\n",
    "\n",
    "Note how the order of the for and if statements is the same in both these snippets.\n",
    "\n",
    "If the expression is a tuple (e.g. the `(x, y)` in the previous example), it must be parenthesized.\n",
    "\n",
    "\t>>>\n",
    "\t>>> vec = [-4, -2, 0, 2, 4]\n",
    "\t>>> # create a new list with the values doubled\n",
    "\t>>> [x*2 for x in vec]\n",
    "\t[-8, -4, 0, 4, 8]\n",
    "\t>>> # filter the list to exclude negative numbers\n",
    "\t>>> [x for x in vec if x >= 0]\n",
    "\t[0, 2, 4]\n",
    "\t>>> # apply a function to all the elements\n",
    "\t>>> [abs(x) for x in vec]\n",
    "\t[4, 2, 0, 2, 4]\n",
    "\t>>> # call a method on each element\n",
    "\t>>> freshfruit = ['  banana', '  loganberry ', 'passion fruit  ']\n",
    "\t>>> [weapon.strip() for weapon in freshfruit]\n",
    "\t['banana', 'loganberry', 'passion fruit']\n",
    "\t>>> # create a list of 2-tuples like (number, square)\n",
    "\t>>> [(x, x**2) for x in range(6)]\n",
    "\t[(0, 0), (1, 1), (2, 4), (3, 9), (4, 16), (5, 25)]\n",
    "\t>>> # the tuple must be parenthesized, otherwise an error is raised\n",
    "\t>>> [x, x**2 for x in range(6)]\n",
    "\t  File \"<stdin>\", line 1\n",
    "\t    [x, x**2 for x in range(6)]\n",
    "\t               ^\n",
    "\tSyntaxError: invalid syntax\n",
    "\t>>> # flatten a list using a listcomp with two 'for'\n",
    "\t>>> vec = [[1,2,3], [4,5,6], [7,8,9]]\n",
    "\t>>> [num for elem in vec for num in elem]\n",
    "\t[1, 2, 3, 4, 5, 6, 7, 8, 9]\n",
    "\n",
    "List comprehensions can contain complex expressions and nested functions:\n",
    "\n",
    "\t>>>\n",
    "\t>>> from math import pi\n",
    "\t>>> [str(round(pi, i)) for i in range(1, 6)]\n",
    "\t['3.1', '3.14', '3.142', '3.1416', '3.14159']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<a id=\"script\"></a>\n",
    "\n",
    "### Python Script Example\n",
    "\n",
    "This is a survey for Python students. We will work though this example script then execute it from the command line.\n",
    "\n",
    "```python\n",
    "# survey_sio209.py\n",
    "\n",
    "print(\"\"\"\n",
    "#################################\n",
    "SIO 209: Python for Data Analysis\n",
    "#################################\n",
    "\n",
    "Please answer the following questions.\n",
    "\"\"\")\n",
    "\n",
    "def get_free_text(prompt):\n",
    "    answer = input(prompt)\n",
    "    return answer\n",
    "\n",
    "def get_score(prompt):\n",
    "    while True:\n",
    "        answer = input(prompt)\n",
    "        if answer in [\"0\", \"1\", \"2\", \"3\"]:\n",
    "            break\n",
    "        else:\n",
    "            print(\"You must select a number from 0 to 3.\")\n",
    "    return answer\n",
    "\n",
    "def get_yes_no(prompt):\n",
    "    answer = \"\"\n",
    "    while answer not in [\"Y\", \"N\"]:\n",
    "        answer = input(prompt)\n",
    "        answer = answer[0].upper()\n",
    "    return answer\n",
    "\n",
    "def get_computer():\n",
    "    answer = \"\"\n",
    "    while answer not in [\"Mac\", \"Linux\", \"Windows\"]:\n",
    "        answer = input(\n",
    "            \"Which operating system does your computer run? \" + \n",
    "            \"(Mac, Linux, Windows) \\n> \")\n",
    "    return answer\n",
    "\n",
    "first_name = get_free_text(\"First name? \\n> \")\n",
    "last_name = get_free_text(\"Last name? \\n> \")\n",
    "computer_yes_no = get_yes_no(\"Do you have your own laptop? (Y, N) \\n> \")\n",
    "computer_operating_system = get_computer()\n",
    "\n",
    "print(\"\"\"\n",
    "For the following questions, select a number from 0 to 3, where\n",
    "\\t0 - none\n",
    "\\t1 - some\n",
    "\\t2 - moderate\n",
    "\\t3 - experienced\n",
    "\n",
    "What is your experience with the following:\"\"\")\n",
    "\n",
    "score_command_line = get_score(\"Command line? \\n> \")\n",
    "score_bash = get_score(\"Bash? \\n> \")\n",
    "score_r = get_score(\"R? \\n> \")\n",
    "score_matlab = get_score(\"MATLAB? \\n> \")\n",
    "score_perl = get_score(\"Perl? \\n> \")\n",
    "score_python = get_score(\"Python? \\n> \")\n",
    "\n",
    "answers = {'name_first': first_name, \n",
    "           'name_last': last_name, \n",
    "           'computer_has': computer_yes_no, \n",
    "           'computer_os': computer_operating_system, \n",
    "           'score_command': score_command_line, \n",
    "           'score_bash': score_bash, \n",
    "           'score_r': score_r, \n",
    "           'score_matlab': score_matlab, \n",
    "           'score_perl': score_perl, \n",
    "           'score_python': score_python}\n",
    "keys = answers.keys()\n",
    "keys = sorted(keys)\n",
    "\n",
    "outfile = \"answers_\" + first_name + \"_\" + last_name + \".csv\"\n",
    "f = open(outfile, 'w')\n",
    "for key in keys:\n",
    "    f.write(\"%s,%s\\n\" % (key, answers[key]))\n",
    "f.close()\n",
    "\n",
    "print(\"\"\"\n",
    "Thank you for completing the survey!\n",
    "\n",
    "Your answers have been stored in the file '%s'.\n",
    "\"\"\" % outfile)\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<a id=\"ipython\"></a>\n",
    "\n",
    "### IPython Review\n",
    "\n",
    "Plus a few new features..."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### IPython Interpreter\n",
    "\n",
    "Launch `ipython`, which will use IPython for the version of Python that your environment is using. When you launch, notice the version information and the '?' option:\n",
    "\n",
    "\tPython 3.6.4 |Anaconda, Inc.| (default, Jan 16 2018, 12:04:33) \n",
    "    Type 'copyright', 'credits' or 'license' for more information\n",
    "    IPython 6.2.1 -- An enhanced Interactive Python. Type '?' for help.\n",
    "\n",
    "**Tab completion** is a major feature of IPython. Here are some examples from McKinney Ch. 3:\n",
    "\n",
    "\tIn [1]: an_apple = 27 \n",
    "\tIn [2]: an_example = 42\n",
    "\tIn [3]: an<Tab>\n",
    "\n",
    "\tIn [4]: b = [1, 2, 3]\n",
    "\tIn [5]: b.<Tab>\n",
    "\n",
    "\tIn [6]: import datetime\n",
    "\tIn [7]: datetime.<Tab>\n",
    "\n",
    "**Introspection** is what we do with the question mark (?) after an object. One question mark `object?` shows us the docstring: details about the object and its use. Two question marks `object??` shows us the source code: the actual code the source code, if available.  ? and ?? can be used with *any* object in Python.\n",
    "\n",
    "**The %run command** allows you to run a Python program from within IPython. This is similar to running at a system prompt `python file args`, but with the advantage of giving you IPython’s tracebacks, and of loading all variables into your interactive namespace for further use (unless -p is used). Try using `%run` with an existing Python script. Example:\n",
    "\n",
    "    # ex.py\n",
    "    \n",
    "    def add_two(a, b):\n",
    "        return(a+b)\n",
    "\n",
    "    x=5\n",
    "\n",
    "Check that you can access the variables created by running `dir()` to return the names in the current scope (note that `%who` is far more useful). \n",
    "\n",
    "\tIn [1]: %run regex_ex4.py\n",
    "\tIn [2]: dir()\n",
    "\t\n",
    "**Magic commands** are special commands that give you addtional control over your session and computer:\n",
    "\n",
    "\t%quickref  IPython reference card\n",
    "\t%who       Display variables defined in interactive namespace\n",
    "\t%whos      Display variables defined in interactive namespace with types and info\n",
    "\t%reset     Delete all variables in interactive namespace\n",
    "\t%hist      Print command history\n",
    "\t%run       Run a Python script inside IPython\n",
    "\t%magic     Documentation on all the magic commands\n",
    "\t!pwd       Execute pwd or any other system shell command\n",
    "\n",
    "**Command history** is recalled interactively. Just type the first few letters of your command, then the up-arrow, to see all the previous commands you've typed that begin with those letters.\n",
    "\n",
    "**Input and output variables** are stored for use later on:\n",
    "\n",
    "\tIn [1]: 2 ** 27\n",
    "\tOut[1]: 134217728\n",
    "\t\n",
    "\tIn [2]: In[1]       # input of command 1\n",
    "\tOut[2]: u'2 ** 27'\n",
    "\t\n",
    "\tIn [3]: Out[1]      # output of command 1\n",
    "\tOut[3]: 134217728\n",
    "\t\n",
    "\tIn [4]: _           # previous output\n",
    "\tOut[4]: 134217728\n",
    "\t\n",
    "\tIn [5]: __          # previous previous output\n",
    "\tOut[5]: 134217728\n",
    "\t\n",
    "\tIn [6]: _1          # output of command 1\n",
    "\tOut[6]: 134217728\n",
    "\t\n",
    "\tIn [7]: _i1         # input of command 1\n",
    "\tOut[7]: u'2 ** 27'\n",
    "    \n",
    "Note that the output must exist for `_1` notation to work. If a command does not produce output, it will not have an entry in the `Out` dictionary."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<a id=\"jupyter\"></a>\n",
    "\n",
    "### Jupyter Notebook Command Review\n",
    "\n",
    "![commands](https://media.cheatography.com/storage/thumb/weidadeyue_jupyter-notebook.750.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<a id=\"star\"></a>\n",
    "\n",
    "### Star Operator\n",
    "\n",
    "To be run from the notebook. Adapted from [Stack Overflow](http://stackoverflow.com/questions/2921847/what-does-the-star-operator-mean). Also see the [Python Documentation](https://docs.python.org/3/tutorial/controlflow.html#more-on-defining-functions)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The single star `*` unpacks the sequence/collection into **positional arguments**, so you can do this:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def sum(a, b):\n",
    "    return a + b\n",
    "\n",
    "values = (1, 2)\n",
    "\n",
    "sum(*values)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This will unpack the tuple so that it actually executes as:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sum(1, 2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The double star `**` does the same, only using a dictionary and thus **named arguments**:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "values = {'a': 1, 'b': 2}\n",
    "sum(**values)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can also combine:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "28"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def sum(a, b, c, d):\n",
    "    return a + b + c + d\n",
    "\n",
    "values1 = (1, 2)\n",
    "values2 = {'c': 10, 'd': 15}\n",
    "\n",
    "sum(*values1, **values2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "will execute as:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "28"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sum(1, 2, c=10, d=15)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Additionally, you can define functions to take `*x` and `**y` arguments. This allows a function to accept any number of positional and/or named arguments that aren't specifically named in the declaration. \n",
    "\n",
    "Example with `*` (positional arguments):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "15"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def sum(*values):\n",
    "    s = 0\n",
    "    for v in values:\n",
    "        s = s + v\n",
    "    return s\n",
    "\n",
    "sum(1, 2, 3, 4, 5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Example with `**` (named arguments):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def get_a(**values):\n",
    "    return values['a']\n",
    "\n",
    "get_a(a=1, b=2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This can allow you to specify **optional parameters** without having to declare them. And again, you can combine `*` and `**`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "def sum(*values, **options):\n",
    "    s = 0\n",
    "    for i in values:\n",
    "        s = s + i\n",
    "    if \"neg\" in options:\n",
    "        if options[\"neg\"]:\n",
    "            s = -s\n",
    "    return s"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "15"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sum(1, 2, 3, 4, 5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "-15"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sum(1, 2, 3, 4, 5, neg=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "15"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sum(1, 2, 3, 4, 5, neg=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "anaconda-cloud": {},
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
